; boot.asm:引导程序，被BIOS加载到0x7C00处执行
; boot.asm 只能位于引导分区的第一个扇区
;   对于软盘来说是512字节，实际可用的是510字节，末尾2字节为0xAA55，为引导分区标志
;   对于硬盘来说，也是512字节，但是硬盘需要预留64字节作为分区标记，因此实际可用的只有436（去除64字节和末尾的标志位2字节0xAA55）
; 本文件的boot将实现加载第2个扇区开始的共计SystemSectorNum个扇区到SystemStartupAddress

; system模块占用的扇区个数，从第2个扇区开始
;SystemSectorNum         equ         200        ; 大小共100K
SystemSectorNum         equ         72          ; bios目前限制最大一次只能读取72个扇区，临时解决方案

; system模块加载到内存的地址
SystemStartupAddress    equ         01000h      ; 位于64K处，1 0000 0000 0000 0000

    org 07c00h                      ; BIOS会跳到此处执行，但是CS段仍指向0，IP指向0x7c00，因此boot内的偏移需要以0x7c00为基准
    mov ax, cs
    mov ds, ax
    mov es, ax

    call ClearScreen
    
    mov ax, LoadSystemMessage       ; 待显示字符串
    mov cx, LoadSystemMessageLen    ; 待显示字符串长度
    mov dx, 0000h                   ; 显示起始位置，0行0列
    call DispStr

    ; 加载system模块到64K处
    call LoadSystem
    cmp ah, 0                       ; 加载失败错误码存在ax中
    je .show_load_ok_message        ; 加载成功
.show_load_fail_message:
    mov ax, LoadSystemFail          ; 待显示字符串
    mov cx, LoadSystemFailLen       ; 待显示字符串长度
    mov dx, 0100h                   ; 显示起始位置，1行0列
    call DispStr

    jmp $                           ; 加载失败先直接死循环

.show_load_ok_message:
    mov ax, LoadSystemOk            ; 待显示字符串
    mov cx, LoadSystemOkLen         ; 待显示字符串长度
    mov dx, 0100h                   ; 显示起始位置，1行0列
    call DispStr

    mov ax, JumpToSys               ; 待显示字符串
    mov cx, JumpToSysLen            ; 待显示字符串长度
    mov dx, 0200h                   ; 显示起始位置，1行0列
    call DispStr

    ; 跳到system模块执行
    jmp dword SystemStartupAddress:0


;====================================================================
; 定义一些待显示的字符串
LoadSystemMessage:          db  "Loading SYSTEM..."
LoadSystemMessageLen        equ $ - LoadSystemMessage
LoadSystemFail:             db  "Load SYSTEM fail!!!"
LoadSystemFailLen           equ $ - LoadSystemFail
LoadSystemOk:               db  "Load SYSTEM OK~~~"
LoadSystemOkLen             equ $ - LoadSystemOk
JumpToSys:                  db  "Now jump to SYSTEM..."
JumpToSysLen                equ $ - JumpToSys


;=====================================================================
; DispStr：使用BIOS提供的中断，来实现字符串显示操作
; 前提：假设带显示的字符串已经位于ax寄存器中，cx中存放待显示字符的长度，dx存放起始坐标
; 参数介绍
; ah = 13h，功能码，显示字符串
; al = 显示方式。 1）0表示字符串中只包含字符，显示属性在bl中，光标位置不变；
;               2）1表示字符串中只包含字符，显示属性在bl中，光标位置改变；
;               3）2表示字符串中包含显示字符和属性，显示后，光标位置不变；
;               4）3表示字符串中包含显示字符和属性，显示后，光标位置改变；
; bh = 页码
; bl = 属性
; cx = 待显示字符串的长度
; (dh, dl) = 显示起始坐标(Y坐标, X坐标)
; es:bp = 显示字符串的位置
DispStr:
    mov bp, ax          ; 假设调用前显示字符串的地址已经存放在寄存器ax中
                        ; 假设待显示字符个数存放在cx寄存器中，因此此处不需要设置
    mov ah, 13h         ; 功能码13h
    mov al, 1           ; 显示方式为1：字符串中只包含字符，显示属性在bl中，光标位置改变

    mov bl, 0ch         ; 显示属性：黑底红字
    mov bh, 0           ; 页码 0，当前还没有滚屏操作

                        ; 假设调用前dx已经存放了起始坐标
    int 10h
    ret
; DispStr 结束

;=====================================================================
; ClearScreen：清屏操作，使用BIOS提供的10h中断实现
; 前置条件：无
; 参数介绍：
; ah = 0x06，向上滚屏；ah = 0x07h，向下滚屏
; al = 滚动行数（0表示清窗口）
; bh = 空白区域的缺省属性
; (ch, cl) = 窗口的左上角位置(Y坐标，X坐标)
; (dh, dl) = 窗口的右下角位置(Y坐标，X坐标)
ClearScreen:
    mov ah, 06h
    mov al, 0       ; 清窗口
    mov cl, 0       ; 待清理窗口左上角的位置
    mov ch, 0
    mov dl, 79      ; 待清理窗口右下角的位置，窗口的大小80列 *25行
    mov dh, 24
    mov bh, 07h     ; 黑底白字
    int 10h
    ret
; ClearScreen 结束

;=====================================================================
; LoadSystem：使用BIOS提供的int 0x13中断读扇区，加载system模块
; 参数说明：
;   ah = 0x02 表示读磁盘扇区；al = 待读取的扇区数量
;   ch = 磁道（柱面）号的低8位；cl = 开始扇区(位0-5)，磁道号高2位（位6-7）
;   dh = 磁头号；dl = 驱动器号
;   es:bx 指向数据缓冲区，如果出错那么CF标志位置位
; 返回值：
;   成功则ah=0，al存放读取的扇区数量；失败则ah=出错码
LoadSystem:
    push es
    xor dx, dx                      ; 磁头号0，驱动器号0（代表软盘）
    mov ax, SystemStartupAddress
    mov es, ax                      ; 段基址SystemStartupAddress
    mov ah, 02h                     ; 读扇区操作
    mov al, SystemSectorNum         ; 读SystemSectorNum个扇区
    mov cl, 2                       ; 从第2个扇区开始读
    mov ch, 0                       ; 磁道号是0
    mov bx, 0                       ; 从SystemStartupAddress：0处开始存放
    int 13h
    jnc .load_ok                    ; CF标志位是0表示加载成功
.load_fail:
    ; 加载不成功，先不做处理
    ;jmp $
    ;mov ax, 0101h

.load_ok:
    ; 加载成功，也不做处理
    ;mov ax, 0000h
    pop es
    ret


times 510-($-$$)    db 0
dw 0xaa55